跨站脚本 (XSS) 防御

1. 什么是跨站脚本 (XSS)?

跨站脚本(Cross-Site Scripting,简称 XSS)是一种常见的 Web 安全漏洞,攻击者通过在网页中注入恶意脚本,使得这些脚本在其他用户的浏览器中执行,从而窃取用户信息、篡改网页内容或进行其他恶意操作。

1.1 XSS 的类型

  • 反射型 XSS:恶意脚本通过 URL 参数或表单提交等方式直接反射到页面中,用户点击恶意链接时触发。
  • 存储型 XSS:恶意脚本被存储在服务器端(如数据库),当其他用户访问包含恶意脚本的页面时触发。
  • DOM 型 XSS:恶意脚本通过修改页面的 DOM 结构来执行,通常不涉及服务器端。

2. XSS 的危害

  • 窃取用户信息:攻击者可以获取用户的 Cookie、Session ID 等敏感信息。
  • 篡改网页内容:攻击者可以修改网页内容,欺骗用户或进行钓鱼攻击。
  • 劫持用户会话:攻击者可以劫持用户的会话,冒充用户进行操作。

3. XSS 防御的基本原则

3.1 输入验证

确保用户输入的数据符合预期的格式和类型,避免恶意脚本的注入。

function validateInput(input) {
    // 假设我们只接受字母和数字
    const regex = /^[a-zA-Z0-9]+$/;
    return regex.test(input);
}

const userInput = "Hello123";
if (validateInput(userInput)) {
    console.log("输入有效");
} else {
    console.log("输入无效");
}

3.2 输出编码

在将用户输入的数据插入到 HTML、JavaScript 或 CSS 中时,进行适当的编码,防止恶意脚本的执行。

function encodeHTML(text) {
    return text.replace(/&/g, '&')
               .replace(/</g, '&lt;')
               .replace(/>/g, '&gt;')
               .replace(/"/g, '&quot;')
               .replace(/'/g, '&#039;');
}

const userInput = "<script>alert('XSS')</script>";
const safeHTML = encodeHTML(userInput);
document.getElementById('output').innerHTML = safeHTML;

3.3 使用安全的 API

使用安全的 API 来操作 DOM,避免直接使用 innerHTML 等不安全的属性。

const userInput = "<b>Hello World</b>";
const safeHTML = encodeHTML(userInput);

// 使用安全的 API
document.getElementById('output').textContent = safeHTML;

3.4 内容安全策略 (CSP)

通过设置内容安全策略(Content Security Policy,CSP),限制页面中可以执行的脚本来源,防止恶意脚本的执行。

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">

4. 实践练习

4.1 反射型 XSS 防御

假设我们有一个简单的搜索功能,用户输入的内容会直接显示在页面上。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>反射型 XSS 防御</title>
</head>
<body>
    <form action="/search" method="get">
        <input type="text" name="query" placeholder="搜索内容">
        <button type="submit">搜索</button>
    </form>
    <div id="result"></div>

    <script>
        const urlParams = new URLSearchParams(window.location.search);
        const query = urlParams.get('query');
        if (query) {
            document.getElementById('result').textContent = encodeHTML(query);
        }

        function encodeHTML(text) {
            return text.replace(/&/g, '&amp;')
                       .replace(/</g, '&lt;')
                       .replace(/>/g, '&gt;')
                       .replace(/"/g, '&quot;')
                       .replace(/'/g, '&#039;');
        }
    </script>
</body>
</html>

4.2 存储型 XSS 防御

假设我们有一个评论功能,用户输入的评论会被存储在服务器端,并在页面上显示。

// 服务器端代码 (Node.js)
const express = require('express');
const bodyParser = require('body-parser');
const app = express();

app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static('public'));

let comments = [];

app.post('/comment', (req, res) => {
    const comment = req.body.comment;
    comments.push(encodeHTML(comment));
    res.redirect('/');
});

app.get('/', (req, res) => {
    res.send(`
        <form action="/comment" method="post">
            <input type="text" name="comment" placeholder="输入评论">
            <button type="submit">提交</button>
        </form>
        <div id="comments">
            ${comments.map(comment => `<p>${comment}</p>`).join('')}
        </div>
    `);
});

function encodeHTML(text) {
    return text.replace(/&/g, '&amp;')
               .replace(/</g, '&lt;')
               .replace(/>/g, '&gt;')
               .replace(/"/g, '&quot;')
               .replace(/'/g, '&#039;');
}

app.listen(3000, () => {
    console.log('服务器运行在 http://localhost:3000');
});

5. 总结

跨站脚本(XSS)是一种常见的 Web 安全漏洞,攻击者可以通过注入恶意脚本来窃取用户信息、篡改网页内容或进行其他恶意操作。为了防御 XSS,我们需要遵循以下原则:

  • 输入验证:确保用户输入的数据符合预期的格式和类型。
  • 输出编码:在将用户输入的数据插入到 HTML、JavaScript 或 CSS 中时,进行适当的编码。
  • 使用安全的 API:避免直接使用 innerHTML 等不安全的属性。
  • 内容安全策略 (CSP):通过设置 CSP 限制页面中可以执行的脚本来源。

通过这些措施,我们可以有效地防御 XSS 攻击,保护用户的安全。